//
//  MapAlgorithm.m
//  SPMapKit
//
//  Created by zhangbo on 2017/9/7.
//  Copyright © 2017年 zhangbo. All rights reserved.
//

#import "MapAlgorithm.h"
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@implementation Tile

@end

//@implementation MapAlgorithm
//
//@end


double Clip(double n,double minValue, double maxValue)
{
    return MIN(MAX(n,minValue), maxValue);
}

uint MapSize(int levelOfDetail)
{
    return (uint) 256 <<levelOfDetail;
}

double GroundResolution(double latitude, int levelOfDetail)
{
    latitude = Clip(latitude,MinLatitude, MaxLatitude);
    return cos(latitude * M_PI/ 180) * 2 * M_PI * EarthRadius / MapSize(levelOfDetail);
}

double MapScale(double latitude, int levelOfDetail, int screenDpi)
{
    return GroundResolution(latitude,levelOfDetail) * screenDpi / 0.0254;
}

void LatLongToPixelXY(double latitude, double longitude, int levelOfDetail, double* pixelX, double* pixelY)
{
    latitude = Clip(latitude,MinLatitude, MaxLatitude);
    longitude = Clip(longitude,MinLongitude, MaxLongitude);
    
    double x = (longitude + 180) / 360;
    double sinLatitude =sin(latitude * M_PI / 180);
    double y = 0.5 - log((1 +sinLatitude) / (1 - sinLatitude)) / (4 * M_PI);
    
    uint mapSize =MapSize(levelOfDetail);
    *pixelX = Clip(x * mapSize +0.5, 0, mapSize - 1);
    *pixelY = Clip(y * mapSize +0.5, 0, mapSize - 1);
}

void PixelXYToLatLong(double pixelX, double pixelY, int levelOfDetail, double* latitude, double* longitude)
{
    double mapSize = MapSize(levelOfDetail);
    double x = (Clip(pixelX, 0, mapSize - 1) / mapSize) - 0.5;
    double y = 0.5 - (Clip(pixelY, 0, mapSize - 1) / mapSize);
    
    *latitude = 90 - 360 * atan(exp(-y * 2 * M_PI)) / M_PI;
    *longitude = 360 * x;
}

void PixelXYToTileXY(int pixelX, int pixelY, int* tileX, int* tileY)
{
    *tileX = pixelX / 256;
    *tileY = pixelY / 256;
}

void TileXYToPixelXY(int tileX, int tileY, int* pixelX, int* pixelY)
{
    *pixelX = tileX * 256;
    *pixelY = tileY * 256;
}

void QuadKeyToTileXY(char* quadKey, int* tileX, int* tileY, int* levelOfDetail)
{
    tileX = tileY = 0;
    *levelOfDetail = (int)strlen(quadKey);
    for (int i = *levelOfDetail; i > 0; i--)
    {
        int mask = 1 << (i - 1);
        switch (quadKey[*levelOfDetail - i])
        {
            case '0':
                break;
                
            case '1':
                *tileX |= mask;
                break;
                
            case '2':
                *tileY |= mask;
                break;
                
            case '3':
                *tileX |= mask;
                *tileY |= mask;
                break;
                
            default:
                break;
        }
    }
}

bool outOfChina(double lat, double lon) {
    if (lon < 72.004 || lon > 137.8347)
        return true;
    if (lat < 0.8293 || lat > 55.8271)
        return true;
    return false;
}

double transformLat(double x, double y) {
    double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(fabs(x));
    ret += (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0;
    ret += (20.0 * sin(y * M_PI) + 40.0 * sin(y / 3.0 * M_PI)) * 2.0 / 3.0;
    ret += (160.0 * sin(y / 12.0 * M_PI) + 320 * sin(y * M_PI / 30.0)) * 2.0 / 3.0;
    return ret;
}

double transformLon(double x, double y) {
    double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(fabs(x));
    ret += (20.0 * sin(6.0 * x * M_PI) + 20.0 * sin(2.0 * x * M_PI)) * 2.0 / 3.0;
    ret += (20.0 * sin(x * M_PI) + 40.0 * sin(x / 3.0 * M_PI)) * 2.0 / 3.0;
    ret += (150.0 * sin(x / 12.0 * M_PI) + 300.0 * sin(x / 30.0 * M_PI)) * 2.0 / 3.0;
    return ret;
}

void convertGPS84_To_Gcj02(double x, double y, double* transX, double* transY)
{
    if (outOfChina(x, y)) {
        // 出国就用GPS84
        *transX = x;
        *transY = y;
    }else{
        double lat = x;
        double lon = y;
        double ee = 0.00669342162296594323;
        static double a = 6378245.0;
        
        double dLat = transformLat(lon - 105.0, lat - 35.0);
        double dLon = transformLon(lon - 105.0, lat - 35.0);
        double radLat = lat / 180.0 * M_PI;
        double magic = sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * M_PI);
        dLon = (dLon * 180.0) / (a / sqrtMagic * cos(radLat) * M_PI);
        *transX = lat + dLat;
        *transY = lon + dLon;
    }
}

void convertGcj02_To_GPS84(double x, double y, double* transX, double* transY)
{
    double mgLat = 0;
    double mgLon = 0;
    
    convertGPS84_To_Gcj02(x, y, &mgLat, &mgLon);
    
    double dLat = mgLat - x;
    double dLon = mgLon - y;
    
    *transX = x - dLat;
    *transY = y - dLon;
}

 double pointToSegDist(double x, double y, double x1, double y1, double x2, double y2)
{
    double cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
    if (cross <= 0) return sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
    
    double d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
    if (cross >= d2) return sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
    
    double r = cross / d2;
    double px = x1 + (x2 - x1) * r;
    double py = y1 + (y2 - y1) * r;
    return sqrt((x - px) * (x - px) + (py - y) * (py - y));
}
